﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Collections;

namespace SharePoint.SPSilver
{
    public class ClassIngredients
    {

        public String Title { get { return contentTypeName; } }
        public override String ToString() { return Title; }
        public SPList list{ get; private set;}
        public String listClassName { 
            get 
            {
                string _listName = this.list.RootFolder.Name.Replace(" ", "");
                if (_listName == contentTypeName)
                {
                    if (_listName.Substring(_listName.Length - 1).ToLower() == "s")
                    {
                        contentTypeName += "_0";
                    }
                    else
                    {
                        _listName += "s";
                    }
                }
                return _listName;
            } 
        }
        public System.Collections.IList selectedFields { get; private set; }
        public String contentTypeName { get; private set; }
        public String namespaceName { get; private set; }
        public ContentType originalContentTypeName { get; set; }

        public ClassIngredients(SPList _list, 
                                System.Collections.IList _selectedFields, 
                                String _contentTypeName, 
                                ContentType _originalContentTypeName, 
                                String _namespaceName)
        {
            list = _list;
            selectedFields = _selectedFields;
            contentTypeName = _contentTypeName;
            namespaceName = _namespaceName;
            originalContentTypeName = _originalContentTypeName;
        }

        public String buildOtherMembers()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat(@"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Taxonomy;
using System.Web;
using SPSilver.Common;
// using Unisystems.SharePoint.CustomFields;
// using Unisystems.SharePoint.Common.Logging;

namespace {0}
{{
    public partial class {1}
    {{
        // your methods & properties go here
    }}
    public partial class {2}
    {{
        // your methods & properties go here
    }}
}}",
        this.namespaceName,
        this.listClassName,
        this.contentTypeName);
            return sb.ToString();
        }

        public String buildClassCode(List<ClassIngredients> classList, Boolean isReadOnly, Boolean includeListDefinition)
        {

            StringBuilder sb = new StringBuilder();
            IList selectedFields = this.selectedFields;
            String listInternalName = this.listClassName; //.Replace(" ", "") //any other invalid characters?
            String listDisplayName = this.list.Title;
            String listUrl = this.list.RootFolder.Url;
            String listWebUrl = this.list.ParentWebUrl;
            if (listWebUrl[listWebUrl.Length - 1] != '/')
                listWebUrl = listWebUrl + '/';
            String contentTypeName = this.contentTypeName;
            String namespaceName = this.namespaceName;
            Boolean isLookup = false;
            Boolean isUsingTaxonomy = false;

            {
                String thisListGuid = String.Format("{{{0}}}", this.list.ID.ToString().ToUpper());
                bool breakAll = false;
                foreach (ClassIngredients ci in classList)
                {
                    foreach (var foo in ci.selectedFields)
                    {
                        SPField f = null;
                        if (foo is SPField)
                            f = (SPField)foo;
                        else if (foo is SPField_Extended)
                            f = (foo as SPField_Extended).field;
                        // var f = f_extended.field;
                        if (f.GetType().Name.ToLower().Contains("taxonomy"))
                            isUsingTaxonomy = true;
                        if (f is SPFieldLookup)
                        {
                            if ((f as SPFieldLookup).LookupList.ToUpper() == thisListGuid.ToUpper())
                            {
                                isLookup = true;
                                breakAll = true;
                                break;
                            }

                        }
                    }
                    if (breakAll) break;
                }
            }
            sb.AppendFormat(@"
    // **********************************************************
    // *** This file has been automatically generated on {0}
    // *** You should *Avoid* making manual changes to this file
    // *** Please use {0}.otherMembers.cs instead
    // **********************************************************
    ", listClassName, DateTime.Now.ToString());

            sb.AppendFormat(@"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.ComponentModel;
using System.Collections;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
{0}using Microsoft.SharePoint.Taxonomy;",(isUsingTaxonomy?"":"// "));

            sb.AppendFormat(@"
using SPSilver.Common;
// using Unisystems.SharePoint.CustomFields;
// using Unisystems.SharePoint.Common.Logging;

namespace {1}
{{

    /// <summary>
    /// The SharePoint List located at {2}{3}
    /// </summary>
    public partial class {0}: SPSilver.Common.List
    {{",
                listClassName, //0 the internal name of the List eg Articles
                //2 the root url of the List eg /Lists/Articles
                namespaceName,
                listWebUrl,     //2 //it is dynamically assigned, so this is removed
                listUrl);      //3 the root url of the List eg /Lists/Articles);

            if (includeListDefinition)
            {
                sb.AppendFormat(@"
        // LoggingService log = new LoggingService(LoggingService.Site.unknown, ""{0}"");

        /// <summary>
        /// The List display Name
        /// </summary>
        public static readonly String Name = ""{1}"";
        /// <summary>
        /// The list internal name in a url format
        /// </summary>
        public static readonly String URL = ""/{3}"";",
                listClassName, //0 the internal name of the List eg Articles
                listDisplayName, //1 the display name of the List eg Άρθρα
                listWebUrl,     //2 //it is dynamically assigned, so this is removed
                listUrl,      //3 the root url of the List eg /Lists/Articles
                namespaceName ); //4
                sb.Append(@"
        public override SPList list { get; protected set; }");

                sb.AppendFormat(@"
        /// <summary> 
        /// Default constructor of the ContentType factory
        /// </summary> 
        public {0} (SPWeb web)
        {{
            if (web != null)
            {{
                if (web.Url == ""/"")
                    list = web.GetList(URL);
                else
                    list = web.GetList(web.Url + URL);
            }}
            else
                throw new HttpException(500, this.GetType().Name + ""(ctor) the provided web object was null"");
        }}",
                listClassName,
                contentTypeName
                );
            }
                sb.AppendFormat(@"
        public {1} get{2}ById(int id)
        {{
            {1} f = null;
            if (id > 0)
            {{
                try{{
                SPListItem i = this.list.GetItemById(id);
                f = new {1}(i);
                }}catch{{}}// Item was not found, return null ? or raise exception?
            }}
            return f;
        }}",
            listClassName,
            contentTypeName,
            (includeListDefinition ? "" : contentTypeName)
            );

            String spquery_viewfields = String.Empty;
            {
                StringBuilder view_sb = new StringBuilder("qry.ViewFields = \"<FieldRef Name='Editor' /><FieldRef Name='Author' /><FieldRef Name='Created' /><FieldRef Name='Modified' /><FieldRef Name='Title' /><FieldRef Name='UniqueId' />");
                /*
                 * Editor
                 * Author
                 * Created
                 * Modified
                 * Title
                 * UniqueId
                 */
                foreach (var foo in selectedFields)
                {
                    SPField o = null;
                    if (foo is SPField)
                        o = (SPField)foo;
                    else if (foo is SPField_Extended)
                        o = (foo as SPField_Extended).field;

                    if (o.Title != "Title")
                    {
                        Field f = new Field(o as SPField, classList);
                        view_sb.AppendFormat("<FieldRef Name='{0}' />", f.newPropertyName);
                    }
                }
                view_sb.Append(@""";
                        ");
                spquery_viewfields = view_sb.ToString();
            }

            sb.AppendFormat(@" 
        public ListOf{0} get{1}ByIds(SPFieldLookupValueCollection lookups)
        {{
            ListOf{0} objects = new ListOf{0}();
            foreach (SPFieldLookupValue lookupValue in lookups)
            {{
                objects.Add(new {0}(this.list.GetItemById(lookupValue.LookupId)));
            }}
            return objects;
        }}
        ", contentTypeName,
            (includeListDefinition ? "" : contentTypeName));
            foreach (var foo in selectedFields)
            {
                SPField o = null;
                if (foo is SPField)
                    o = (SPField)foo;
                else if (foo is SPField_Extended)
                    o = (foo as SPField_Extended).field;

                if (o.Indexed)
                {
                    Field f = new Field(o as SPField, classList);
                    sb.AppendFormat(@"
        ///<summary> WARNING! this is non-optimized code! Use with caution.</summary>
        public ListOf{0} get{7}By{1}({2} filterValue)
        {{
            ListOf{0} oList = new ListOf{0}();
            if (filterValue != null)
            {{
                try
                {{
                    if (list != null) 
                    {{ 
                        SPQuery qry = new SPQuery(); 
                        // qry.ViewAttributes = ""Scope='Recursive'"";
                        qry.ViewFieldsOnly = true;
                        {6}qry.Query = 
                        String.Format(""<Where><Eq><FieldRef Name='{1}'{4} /><Value Type='{3}'>{{0}}</Value></Eq></Where>"", 
                                        filterValue{5}); 
                        SPListItemCollection listItems = list.GetItems(qry);
                        oList = convert(listItems, oList);  
                    }} 
                }}catch{{}}// Item was not found, return null ? or raise exception?
            }}
            return oList;
        }}",
                    contentTypeName,
                    f.newPropertyName,
                    f.cSharpType(),
                    f.TypeAsString,
                    ((f.Type == SPFieldType.Lookup) ? " LookupId='True'" : String.Empty),
                    ((f.Type == SPFieldType.Lookup) ? 
                        (f.newPropertyType == "SPFieldLookupValue"? ".LookupId":".ID"):
                            (f.Type == SPFieldType.DateTime ? ".ToString(\"yyyy-MM-ddTHH:mm:ssZ\")" :
                                String.Empty)),
                    spquery_viewfields,
                    (includeListDefinition ? "" : contentTypeName)
                    );
                }
            }
            sb.AppendFormat(@"
        public ListOf{0} convert{1}(SPListItemCollection listItems, ListOf{0} oList)
        {{
            if (oList == null) oList = new ListOf{0}();
            foreach(SPListItem i in listItems)
            {{
                try
                {{
                    oList.Add(new {0}(i));
                }}
                catch{{}}// SPlistItem was not valid for conversion, maybe log and continue with the rest of the items
            }}
            return oList;
        }}", 
           contentTypeName,
           (includeListDefinition ? "" : contentTypeName));
            if (!isReadOnly)
                sb.AppendFormat(@"
            
        public {0} Update({0} i)
        {{
            if (i.PropertiesHaveChanged)
            {{   
                SPListItem item = ((i.ID>0)?list.GetItemById(i.ID):list.AddItem());
                foreach(DictionaryEntry property in i.AfterProperties)
                {{
                    try{{
                        // option 1
                        item[(Guid)property.Key] = property.Value;
                        // option 2 is not as fast, but more flexible
                        /* if (property.Key.GetType().Name == ""Guid"")
                            item[(Guid)property.Key] = property.Value;
                        else
                            item[(String)property.Key] = property.Value;*/
                    }}catch(Exception e){{/* this filed does not exist?, is readonly? */}}
                }}
                item.Update();
                i = get{1}ById(item.ID);
            }}
            return i;
        }}
        ",
         contentTypeName,
           (includeListDefinition ? "" : contentTypeName)
         ); if (!isReadOnly)
                sb.AppendFormat(@"
            
        public {0} Delete({0} i)
        {{
            SPListItem item = list.GetItemById(i.ID);
            item.Delete();
            list.Update();
            return null;
        }}
        ",
         contentTypeName
         );
            if (!isReadOnly)
            sb.AppendFormat(@"
        
        public {0} Create{1}()
        {{
            {0} a = null;
            SPListItem i = list.AddItem();
            a = new {0}(i);
            return a;
        }}", 
            contentTypeName,
            (includeListDefinition ? "" : contentTypeName));
            sb.Append(@"
    }");

            sb.AppendFormat(@"
    /// <summary>
    /// The SharePoint ContentType {0}
    /// </summary>
    public partial class {0} : SPSilver.Common.Item{1}
    {{
        public new class Fields : SPSilver.Common.Item.Fields
        {{
                ", 
                 contentTypeName, 
                 ((isReadOnly) ? String.Empty : ", INotifyPropertyChanged"));
            // fields
            // /// <summary>Πεδίο Enterprise Keywords τύπου Microsoft.SharePoint.Taxonomy.TaxonomyFie...</summary>
            //  public static readonly String TaxKeyword = ""TaxKeyword"";
            foreach (var foo in selectedFields)
            {
                SPField o = null;
                if (foo is SPField)
                    o = (SPField)foo;
                else if (foo is SPField_Extended)
                    o = (foo as SPField_Extended).field;

                Field f = new Field(o as SPField, null);
                sb.AppendFormat(@"
            /// <summary>
            /// InternalName: {5}
            /// Descr: {0} 
            /// TypeAsString: {1}
            /// GUID: {2}
            /// Is Indexed: {3}
            /// Display name: {4}
            /// </summary>", f.Description, f.TypeAsString, f.Id, f.Indexed, f.DisplayName, f.InternalName);
                sb.AppendFormat(@"
            public{1} static readonly String {0} = ""{0}"";", f.InternalName, f.isNew);
                sb.AppendFormat(@"
            public static readonly Guid {0}_Guid = new Guid(""{1}"");", f.InternalName, f.Id.ToString());
                sb.AppendFormat(@"
            public static readonly string {0}_DisplayName = ""{1}"";", f.InternalName, f.DisplayName.Replace("\"","\\\""));
            }
            sb.Append(@"
        }");
            foreach (var foo in selectedFields)
            {
                SPField o = null;
                if (foo is SPField)
                    o = (SPField)foo;
                else if (foo is SPField_Extended)
                    o = (foo as SPField_Extended).field;

                Field f = new Field(o as SPField, classList);
                if (isReadOnly)
                {
                    sb.Append(PropertyReadOnly(f));
                }
                else
                {
                    sb.Append(PropertyReadWrite(f));
                }
            }
            /*sb.AppendFormat(@"
        /// <summary>
        /// default constructor
        /// </summary>
        public {0}(SPListItem i)
        : base(i)
        {{", contentTypeName);
            foreach (Object f in listBoxFields.SelectedItems)
            {
                SPField f = (SPField)f;
                sb.AppendFormat(@"
            try{{{0} = {1};}}catch{{}}", f.InternalName, safeCasting(cSharpType(f), f.InternalName,false));
            }
            sb.Append(@"
        }");*/

            sb.AppendFormat(@"
        /// <summary>
        /// constructor with SPFieldCollection
        /// </summary>
        public {0}(SPListItem i)// , SPFieldCollection _fieldsCol )
        : base(i)
        {{
            // fieldsCol = _fieldsCol;", contentTypeName);
            foreach (var foo in selectedFields)
            {
                SPField o = null;
                if (foo is SPField)
                    o = (SPField)foo;
                else if (foo is SPField_Extended)
                    o = (foo as SPField_Extended).field;

                Field f = new Field(o as SPField, classList);
                sb.AppendFormat(@"
            try{{{2}{0} = {1};}}catch{{}}", 
                                        f.newPropertyName + (f.newPropertyName == f.newPropertyType?"_":""),
                                        f.safeCasting(),
                                        (isReadOnly?String.Empty:"_")
                                        );
            }
            sb.Append(@"
        }");
            if (!isReadOnly)
                sb.Append(@"
        // you may want to override and change the behaviour
        protected override void OnPropertyChanged(String propertyName, Guid propertyGuid, Object propertyValue)
        {
            base.OnPropertyChanged(propertyName, propertyGuid, propertyValue);
        }
        ");
            if (isLookup)
            {
                sb.Append(
                    @"
        /// <summary>
        /// ToString of lookups should return the lookup vlaue
        /// </summary>
        /// <returns></returns>
        public override String ToString()
        {
            String response = String.Empty;
            response = String.Format(""{0};#{1}"",this.ID,this.Title);
            return response;
        }");
            }
            sb.Append(@"
    }");
            sb.AppendFormat(@"
    
    public class ListOf{0}:List<{0}>
    {{
        public override String ToString()
        {{
            StringBuilder response = new StringBuilder();
            foreach ({0} p in this)
            {{
                response.Append(p.ToString());
            }}
            return response.ToString();
        }}

        /// <summary>
        /// Returns the first {0} in the list<> (position 0) or null if the list is empty
        /// </summary>
        public {0} First()
        {{
            if (this.Count > 0)
            {{
                return this[0];
            }}
            return null;
        }}
    }}
    ", contentTypeName);
            sb.Append(@"
}");
            return sb.ToString();
        }

        private String PropertyReadOnly(Field field)
        {
            return String.Format(@"
        /// <summary>
        /// Description: {2} 
        /// TypeAsString: {3}
        /// Type: {4}
        /// </summary>
        public {0} {1} {{ get; private set; }}",
                            field.newPropertyType, 
                            field.newPropertyName,
                            field.Description, 
                            field.TypeAsString,
                            field.Type.ToString());
        }

        private String PropertyReadWrite(Field f)
        {
            String isNew = (f.newPropertyName == "Title") ? " new" : "";
            return String.Format(@"
        {0} _{1};
        /// <summary>
        /// Description: {2} 
        /// TypeAsString: {3}
        /// Type: {4}
        /// </summary>
        public{5} {0} {1} 
        {{ 
            get {{ return _{1}; }} 
            set {{ if (value != _{1}) {{ _{1} = value; OnPropertyChanged(""{1}"", Fields.{6}_Guid, value); }} }}
        }}",
           f.newPropertyType, 
           f.newPropertyName + (f.newPropertyName == f.newPropertyType?"_":""),
           f.Description, 
           f.TypeAsString,
           f.Type.ToString()/*#4*/,
           isNew/*#5*/, 
           f.newPropertyName);
        }
    }


    public class JsonClassIngredients // : ClassIngredients
    {
        private ClassIngredients inner;
        public JsonClassIngredients() { }
        public JsonClassIngredients (ClassIngredients _inner)
                                /*(SPList _list, 
                                System.Collections.IList _selectedFields, 
                                String _contentTypeName, 
                                ContentType _originalContentTypeName, 
                                String _namespaceName) : 
                                base(_list, 
                                _selectedFields, 
                                _contentTypeName, 
                                _originalContentTypeName, 
                                _namespaceName)*/
        {
            inner = _inner;
        }

        public JsonClassIngredients// (ClassIngredients i)
            (SPList _list, 
            System.Collections.IList _selectedFields, 
            String _contentTypeName, 
            ContentType _originalContentTypeName, 
            String _namespaceName) /*:
                                base(i.list,
                                i.selectedFields,
                                i.contentTypeName,
                                i.originalContentTypeName,
                                i.namespaceName)*/
        {
            inner = new ClassIngredients(_list,
                                _selectedFields,
                                _contentTypeName,
                                _originalContentTypeName,
                                _namespaceName);
        }
        String _site = String.Empty;
        public new String site { get { return !String.IsNullOrEmpty(_site) ? _site : inner.list.ParentWeb.Site.Url; } set { _site = value; } }
        String _web = String.Empty;
        public new String web { get { return !String.IsNullOrEmpty(_web) ? _web : inner.list.ParentWebUrl; } set { _web = value; } }
        String _Title = String.Empty;
        public new String Title { get { return !String.IsNullOrEmpty(_Title) ? _Title : inner.contentTypeName; } set { _Title = value; } }
        public override String ToString() { return inner.Title; }

        private new SPList list { get; set; }
        String _listURL = String.Empty;
        public new String listURL { get { return !String.IsNullOrEmpty(_listURL) ? _listURL : inner.list.RootFolder.ToString(); } set { _listURL = value; } }
        String _listClassName = String.Empty;
        public new String listClassName { get { return !String.IsNullOrEmpty(_listClassName) ? _listClassName : inner.list.RootFolder.Name.Replace(" ", ""); } set { _listClassName = value; } }

        private System.Collections.IList selectedFields { get; set; }
        
        List<String> _selectedFields_ = new List<string>();
        public new List<String> selectedFields_
        {
            get
            {
                return (_selectedFields_.Count>0) ? _selectedFields_ : getSelectedFields();
            }
            set { _selectedFields_ = value; }
        }
        List<String> getSelectedFields()
        {
            List<String> temp = new List<string>();
            foreach (var foo in inner.selectedFields)
            {
                SPField f = null;
                if (foo is SPField)
                    f = (SPField)foo;
                else if (foo is SPField_Extended)
                    f = (foo as SPField_Extended).field;
                temp.Add(f.ToString());
            }
            return temp;
        }
        
        String _contentTypeName = String.Empty;
        public new String contentTypeName { get { return !String.IsNullOrEmpty(_contentTypeName) ? _contentTypeName : inner.contentTypeName; } set { _contentTypeName = value; } }
        
        String _namespaceName = String.Empty;
        public new String namespaceName { get { return !String.IsNullOrEmpty(_namespaceName) ? _namespaceName : inner.namespaceName; } set { _namespaceName = value; } }
        
        String _originalContentTypeName = String.Empty;
        public String originalContentTypeName { get { return !String.IsNullOrEmpty(_originalContentTypeName) ? _originalContentTypeName : inner.originalContentTypeName.ToString(); } set { _originalContentTypeName = value; } }

        public ClassIngredients getInner() 
        { 
            if (inner!=null) return inner;
            SPSite site = new SPSite(_site);
            SPWeb web = site.OpenWeb(_web);
            SPList list = web.GetList((_web + "/").Replace("//","/") + listURL);
            ContentType ct = new ContentType(list.ContentTypes[originalContentTypeName]);
            List<SPField> fields = new List<SPField>();
            foreach (String f in selectedFields_)
            {
                foreach (SPField ctField in ct.Fields)
                {
                    if (ctField.Title == f)
                    {
                        fields.Add(ctField);
                        break; // jump to the next field
                    }
                }
                // var newF = ct.Fields[f];
                // fields.Add(ct.Fields[f]);
            }
            web.Dispose();
            site.Dispose(); // this might harm the list object...
            return inner = (new ClassIngredients(list, fields, contentTypeName,
                                        ct, namespaceName));
        }
    }
}
